/* do not edit automatically generated by mc from Indexing.  */
/* Indexing.mod provides a dynamic indexing mechanism for CARDINAL.

Copyright (C) 2003-2023 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.

This file is part of GNU Modula-2.

GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.

GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.

You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
<http://www.gnu.org/licenses/>.  */

#   if !defined (PROC_D)
#      define PROC_D
       typedef void (*PROC_t) (void);
       typedef struct { PROC_t proc; } PROC;
#   endif

#   if !defined (TRUE)
#      define TRUE (1==1)
#   endif

#   if !defined (FALSE)
#      define FALSE (1==0)
#   endif

#include <stddef.h>
#include <stdlib.h>
#   include "GStorage.h"
#   include "Gmcrts.h"
#if defined(__cplusplus)
#   undef NULL
#   define NULL 0
#endif
#define _Indexing_H
#define _Indexing_C

#   include "Glibc.h"
#   include "GStorage.h"
#   include "GSYSTEM.h"
#   include "GM2RTS.h"

typedef struct Indexing_IndexProcedure_p Indexing_IndexProcedure;

#   define MinSize 128
typedef struct Indexing__T2_r Indexing__T2;

typedef void * *Indexing_PtrToAddress;

typedef Indexing__T2 *Indexing_Index;

typedef unsigned char *Indexing_PtrToByte;

typedef void (*Indexing_IndexProcedure_t) (void *);
struct Indexing_IndexProcedure_p { Indexing_IndexProcedure_t proc; };

struct Indexing__T2_r {
                        void *ArrayStart;
                        unsigned int ArraySize;
                        unsigned int Used;
                        unsigned int Low;
                        unsigned int High;
                        unsigned int Debug;
                        unsigned int Map;
                      };


/*
   InitIndex - creates and returns an Index.
*/

extern "C" Indexing_Index Indexing_InitIndex (unsigned int low);

/*
   KillIndex - returns Index to free storage.
*/

extern "C" Indexing_Index Indexing_KillIndex (Indexing_Index i);

/*
   DebugIndex - turns on debugging within an index.
*/

extern "C" Indexing_Index Indexing_DebugIndex (Indexing_Index i);

/*
   InBounds - returns TRUE if indice, n, is within the bounds
              of the dynamic array.
*/

extern "C" unsigned int Indexing_InBounds (Indexing_Index i, unsigned int n);

/*
   HighIndice - returns the last legally accessible indice of this array.
*/

extern "C" unsigned int Indexing_HighIndice (Indexing_Index i);

/*
   LowIndice - returns the first legally accessible indice of this array.
*/

extern "C" unsigned int Indexing_LowIndice (Indexing_Index i);

/*
   PutIndice - places, a, into the dynamic array at position i[n]
*/

extern "C" void Indexing_PutIndice (Indexing_Index i, unsigned int n, void * a);

/*
   GetIndice - retrieves, element i[n] from the dynamic array.
*/

extern "C" void * Indexing_GetIndice (Indexing_Index i, unsigned int n);

/*
   IsIndiceInIndex - returns TRUE if, a, is in the index, i.
*/

extern "C" unsigned int Indexing_IsIndiceInIndex (Indexing_Index i, void * a);

/*
   RemoveIndiceFromIndex - removes, a, from Index, i.
*/

extern "C" void Indexing_RemoveIndiceFromIndex (Indexing_Index i, void * a);

/*
   DeleteIndice - delete i[j] from the array.
*/

extern "C" void Indexing_DeleteIndice (Indexing_Index i, unsigned int j);

/*
   IncludeIndiceIntoIndex - if the indice is not in the index, then
                            add it at the end.
*/

extern "C" void Indexing_IncludeIndiceIntoIndex (Indexing_Index i, void * a);

/*
   ForeachIndiceInIndexDo - for each j indice of i, call procedure p(i[j])
*/

extern "C" void Indexing_ForeachIndiceInIndexDo (Indexing_Index i, Indexing_IndexProcedure p);


/*
   InitIndex - creates and returns an Index.
*/

extern "C" Indexing_Index Indexing_InitIndex (unsigned int low)
{
  Indexing_Index i;

  Storage_ALLOCATE ((void **) &i, sizeof (Indexing__T2));
  i->Low = low;
  i->High = 0;
  i->ArraySize = MinSize;
  Storage_ALLOCATE (&i->ArrayStart, MinSize);
  i->ArrayStart = libc_memset (i->ArrayStart, 0, static_cast<size_t> (i->ArraySize));
  i->Debug = FALSE;
  i->Used = 0;
  i->Map = (unsigned int) 0;
  return i;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   KillIndex - returns Index to free storage.
*/

extern "C" Indexing_Index Indexing_KillIndex (Indexing_Index i)
{
  Storage_DEALLOCATE (&i->ArrayStart, i->ArraySize);
  Storage_DEALLOCATE ((void **) &i, sizeof (Indexing__T2));
  return NULL;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   DebugIndex - turns on debugging within an index.
*/

extern "C" Indexing_Index Indexing_DebugIndex (Indexing_Index i)
{
  i->Debug = TRUE;
  return i;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   InBounds - returns TRUE if indice, n, is within the bounds
              of the dynamic array.
*/

extern "C" unsigned int Indexing_InBounds (Indexing_Index i, unsigned int n)
{
  if (i == NULL)
    {
      M2RTS_HALT (-1);
      __builtin_unreachable ();
    }
  else
    {
      return (n >= i->Low) && (n <= i->High);
    }
  ReturnException ("../../gcc-read-write/gcc/m2/gm2-libs/Indexing.def", 25, 1);
  __builtin_unreachable ();
}


/*
   HighIndice - returns the last legally accessible indice of this array.
*/

extern "C" unsigned int Indexing_HighIndice (Indexing_Index i)
{
  if (i == NULL)
    {
      M2RTS_HALT (-1);
      __builtin_unreachable ();
    }
  else
    {
      return i->High;
    }
  ReturnException ("../../gcc-read-write/gcc/m2/gm2-libs/Indexing.def", 25, 1);
  __builtin_unreachable ();
}


/*
   LowIndice - returns the first legally accessible indice of this array.
*/

extern "C" unsigned int Indexing_LowIndice (Indexing_Index i)
{
  if (i == NULL)
    {
      M2RTS_HALT (-1);
      __builtin_unreachable ();
    }
  else
    {
      return i->Low;
    }
  ReturnException ("../../gcc-read-write/gcc/m2/gm2-libs/Indexing.def", 25, 1);
  __builtin_unreachable ();
}


/*
   PutIndice - places, a, into the dynamic array at position i[n]
*/

extern "C" void Indexing_PutIndice (Indexing_Index i, unsigned int n, void * a)
{
  typedef unsigned int * *PutIndice__T1;

  unsigned int oldSize;
  void * b;
  PutIndice__T1 p;

  if (! (Indexing_InBounds (i, n)))
    {
      /* avoid gcc warning by using compound statement even if not strictly necessary.  */
      if (n < i->Low)
        {
          M2RTS_HALT (-1);
          __builtin_unreachable ();
        }
      else
        {
          oldSize = i->ArraySize;
          while (((n-i->Low)*sizeof (void *)) >= i->ArraySize)
            {
              i->ArraySize = i->ArraySize*2;
            }
          if (oldSize != i->ArraySize)
            {
              /* 
               IF Debug
               THEN
                  printf2('increasing memory hunk from %d to %d
              ',
                          oldSize, ArraySize)
               END ;
  */
              Storage_REALLOCATE (&i->ArrayStart, i->ArraySize);
              /* and initialize the remainder of the array to NIL  */
              b = i->ArrayStart;
              b = reinterpret_cast<void *> (reinterpret_cast<char *> (b)+oldSize);
              b = libc_memset (b, 0, static_cast<size_t> (i->ArraySize-oldSize));
            }
          i->High = n;
        }
    }
  b = i->ArrayStart;
  b = reinterpret_cast<void *> (reinterpret_cast<char *> (b)+(n-i->Low)*sizeof (void *));
  p = static_cast<PutIndice__T1> (b);
  (*p) = reinterpret_cast<unsigned int *> (a);
  i->Used += 1;
  if (i->Debug)
    {
      if (n < 32)
        {
          i->Map |= (1 << (n ));
        }
    }
}


/*
   GetIndice - retrieves, element i[n] from the dynamic array.
*/

extern "C" void * Indexing_GetIndice (Indexing_Index i, unsigned int n)
{
  Indexing_PtrToByte b;
  Indexing_PtrToAddress p;

  if (! (Indexing_InBounds (i, n)))
    {
      M2RTS_HALT (-1);
      __builtin_unreachable ();
    }
  b = static_cast<Indexing_PtrToByte> (i->ArrayStart);
  b += (n-i->Low)*sizeof (void *);
  p = (Indexing_PtrToAddress) (b);
  if (i->Debug)
    {
      if (((n < 32) && (! ((((1 << (n)) & (i->Map)) != 0)))) && ((*p) != NULL))
        {
          M2RTS_HALT (-1);
          __builtin_unreachable ();
        }
    }
  return (*p);
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   IsIndiceInIndex - returns TRUE if, a, is in the index, i.
*/

extern "C" unsigned int Indexing_IsIndiceInIndex (Indexing_Index i, void * a)
{
  unsigned int j;
  Indexing_PtrToByte b;
  Indexing_PtrToAddress p;

  j = i->Low;
  b = static_cast<Indexing_PtrToByte> (i->ArrayStart);
  while (j <= i->High)
    {
      p = (Indexing_PtrToAddress) (b);
      if ((*p) == a)
        {
          return TRUE;
        }
      /* we must not INC(p, ..) as p2c gets confused  */
      b += sizeof (void *);
      j += 1;
    }
  return FALSE;
  /* static analysis guarentees a RETURN statement will be used before here.  */
  __builtin_unreachable ();
}


/*
   RemoveIndiceFromIndex - removes, a, from Index, i.
*/

extern "C" void Indexing_RemoveIndiceFromIndex (Indexing_Index i, void * a)
{
  unsigned int j;
  unsigned int k;
  Indexing_PtrToAddress p;
  Indexing_PtrToByte b;

  j = i->Low;
  b = static_cast<Indexing_PtrToByte> (i->ArrayStart);
  while (j <= i->High)
    {
      p = (Indexing_PtrToAddress) (b);
      b += sizeof (void *);
      if ((*p) == a)
        {
          Indexing_DeleteIndice (i, j);
        }
      j += 1;
    }
}


/*
   DeleteIndice - delete i[j] from the array.
*/

extern "C" void Indexing_DeleteIndice (Indexing_Index i, unsigned int j)
{
  Indexing_PtrToAddress p;
  Indexing_PtrToByte b;

  if (Indexing_InBounds (i, j))
    {
      b = static_cast<Indexing_PtrToByte> (i->ArrayStart);
      b += sizeof (void *)*(j-i->Low);
      p = (Indexing_PtrToAddress) (b);
      b += sizeof (void *);
      p = static_cast<Indexing_PtrToAddress> (libc_memmove (reinterpret_cast<void *> (p), reinterpret_cast<void *> (b), static_cast<size_t> ((i->High-j)*sizeof (void *))));
      i->High -= 1;
      i->Used -= 1;
    }
  else
    {
      M2RTS_HALT (-1);
      __builtin_unreachable ();
    }
}


/*
   IncludeIndiceIntoIndex - if the indice is not in the index, then
                            add it at the end.
*/

extern "C" void Indexing_IncludeIndiceIntoIndex (Indexing_Index i, void * a)
{
  if (! (Indexing_IsIndiceInIndex (i, a)))
    {
      /* avoid gcc warning by using compound statement even if not strictly necessary.  */
      if (i->Used == 0)
        {
          Indexing_PutIndice (i, Indexing_LowIndice (i), a);
        }
      else
        {
          Indexing_PutIndice (i, (Indexing_HighIndice (i))+1, a);
        }
    }
}


/*
   ForeachIndiceInIndexDo - for each j indice of i, call procedure p(i[j])
*/

extern "C" void Indexing_ForeachIndiceInIndexDo (Indexing_Index i, Indexing_IndexProcedure p)
{
  unsigned int j;

  j = Indexing_LowIndice (i);
  while (j <= (Indexing_HighIndice (i)))
    {
      (*p.proc) (Indexing_GetIndice (i, j));
      j += 1;
    }
}

extern "C" void _M2_Indexing_init (__attribute__((unused)) int argc,__attribute__((unused)) char *argv[],__attribute__((unused)) char *envp[])
{
}

extern "C" void _M2_Indexing_fini (__attribute__((unused)) int argc,__attribute__((unused)) char *argv[],__attribute__((unused)) char *envp[])
{
}
